package com.yhy.wxzn.wcs.device.rgv.core.lock;

import com.google.common.collect.Sets;
import com.yhy.wxzn.common.utils.WcsUtils;
import com.yhy.wxzn.wcs.device.plc.task.PlcTaskThread;
import com.yhy.wxzn.wcs.device.rgv.bean.*;
import com.yhy.wxzn.wcs.device.rgv.enums.NodeType;
import com.yhy.wxzn.wcs.entity.WcsTask;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

import static com.yhy.wxzn.wcs.SystemInit.liferToDeviceIdMap;

@Slf4j
public class LockNodeManager {
    private static volatile Set<LockNode> lockedNodes;
    private static volatile Set<LockNode> deviceLockNodes;

    static {
        lockedNodes = Sets.newConcurrentHashSet();
        deviceLockNodes = Sets.newConcurrentHashSet();
    }

    /**
     * 获取未加锁的点
     *
     * @param nodeSet 待检查节点
     * @return
     */
    public static synchronized Set<Coord> getUnLockedNode(Set<Coord> nodeSet) {
        Iterator<Coord> it = nodeSet.iterator();
        while (it.hasNext()) {
            Coord coord = it.next();
            if (lockedNodes.contains(coord)) {
                it.remove();
            }
        }
        return nodeSet;
    }


    /**
     * 获取当前被锁定的节点对象
     *
     * @return LockNode对象
     */
    public synchronized static Set<LockNode> getLockedSetNodes() {
        return lockedNodes;
    }


    public synchronized static List<Node> getLockedListNodes() {
        return lockedNodes.stream().map(lockNode -> new Node(lockNode)).collect(Collectors.toList());
    }
    public synchronized static List<Coord> getLockedNodesByFloor(int floor) {
        ArrayList<Coord> res = new ArrayList<>();
        for (LockNode lockedNode : lockedNodes) {
            if (lockedNode.getZ() == floor) {
                res.add(new Coord(lockedNode.getX(),lockedNode.getY(),lockedNode.getZ()));
            }
        }
        return res;
    }

    /**
     * 获取当前被锁定的节点对象,除了被自己锁定的点
     *
     * @return LockNode对象
     */
    public static Set<LockNode> getLockedNodesExceptSelf(Rgv rgv) {
        Set<LockNode> lockNodeSet = Sets.newHashSet();
        for (LockNode lockNode : lockedNodes) {
            if (!lockNode.getLockedBy().equals(rgv)) {
                lockNodeSet.add(lockNode);
            }
        }
        return lockNodeSet;
    }

    /**
     * 检查该节点集合nodeSet是否被该对象obj锁定
     *
     * @param nodeSet 待检查节点集合
     * @return 被obj占用则返回true, 否则返回false
     */
    public static synchronized <T> boolean isLockedByObj(Set<Coord> nodeSet, T t) {
        boolean isLocked = false;
        for (LockNode lockNode : lockedNodes) {
            for (Coord node : nodeSet) {
                if (lockNode.equals(node) && lockNode.getLockedBy().equals(t)) {
                    isLocked = true;
                    break;
                }
            }
        }
        return isLocked;
    }

    /**
     * 检查该节点集合nodeSet是否被该对象obj锁定
     *
     * @param nodeSet 待检查节点集合
     * @param <T>     被占用则返回 未被占用返回空
     * @return
     */
    public static synchronized <T> T isLocked(Set<Coord> nodeSet) {
        for (LockNode lockNode : lockedNodes) {
            for (Coord node : nodeSet) {
                if (lockNode.equals(node)) {
                    return (T) lockNode.getLockedBy();
                }
            }
        }
        return null;
    }


    /**
     * 直接解锁特定的节点
     *
     * @param node
     */
    public static synchronized void removeLockedNode(Coord node) {
        lockedNodes.remove(node);
    }

    /**
     * 添加进锁定节点集合 会对资源占用进行校验, 若已被自己占用则忽略, 被其他RGV占用则抛出异常
     *
     * @param nodeSet
     */
    public static synchronized void addLockedNodes(Set<Coord> nodeSet, Rgv rgv) {
        updateLockedNodes(nodeSet, true, rgv);
    }

    /**
     * 将占用资源释放 若已被占用且为自己占用则释放, 若为其他RGV占用或未被占用则忽略
     *
     * @param nodeSet
     */
    public static synchronized <T> void removeLockedNodes(Set<Coord> nodeSet, T t) {
        updateLockedNodes(nodeSet, false, t);
    }

    /**
     * 检查待检查节点集合是否存在被占用的节点资源
     *
     * @param nodeSet 待检查节点集合
     * @return 被占用则返回true, 否则返回false
     */
    public static synchronized <T> boolean checkNodesLocked(Set<Coord> nodeSet, T t) {
        for (LockNode lockNode : lockedNodes) {
            for (Coord node : nodeSet) {
                if (lockNode.equals(node) && !lockNode.getLockedBy().equals(t)) {
                    log.info("节点" + node + "被" + lockNode.getLockedBy() + "占用");
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查该节点是否是占用状态
     *
     * @param node 需要检查的节点
     * @return 是否被占用
     */
    public static synchronized boolean checkNodesLocked(Coord node) {
        return lockedNodes.contains(node);
    }

    /**
     * 检查待检查节点集合是否存在被占用的节点资源
     *
     * @param nodes 待检查节点集合
     * @return 被自己占用或者未被占有则返回null, 否则返回占用者
     */
    public static synchronized <T> T checkNodeListLockBy(List<Node> nodes, T t) {
        for (LockNode lockNode : lockedNodes) {
            for (Coord node : nodes) {
                if (lockNode.equals(node) && !lockNode.getLockedBy().equals(t)) {
                    log.info("节点" + node + "被" + lockNode.getLockedBy() + "占用");
                    return (T) lockNode.getLockedBy();
                }
            }
        }
        return null;
    }

    /**
     * node被t锁定，则返回true，否则false
     * @param node
     * @param t
     * @param <T>
     * @return
     */
    public static synchronized <T> boolean checkNodeIsLocked(Node node, T t) {
        for (LockNode lockNode : lockedNodes) {
            if (lockNode.equals(node) && lockNode.getLockedBy().equals(t)) {
                return true;
            }
        }
        return false;
    }



    public static synchronized <T> Set<Coord> getLockCoordByObj(T t) {
        Set<Coord> lockedByObj = new HashSet<>();
        Iterator<LockNode> iterator = lockedNodes.iterator();
        while (iterator.hasNext()) {
            LockNode next = iterator.next();
            if (next.getLockedBy().equals(t)) {
                lockedByObj.add(new Coord(next));
            }
        }
        return lockedByObj;
    }

    public static synchronized Set<FourWayRgv> getLockRgvsByCoords(List<? extends Coord> coords) {
        Set<FourWayRgv> res = new HashSet<>();
        for (LockNode lockedNode : lockedNodes) {
            for (Coord coord : coords) {
                if (Objects.equals(coord,lockedNode)){
                    Object lockedBy = lockedNode.getLockedBy();
                    if (lockedBy instanceof FourWayRgv){
                        res.add((FourWayRgv)lockedBy);
                    }
                }
            }

        }
        return res;
    }

    public static synchronized Object getLockNodeObj(Node node) {
        for (LockNode lockNode : lockedNodes) {
            if (lockNode.equals(node)){
                return lockNode.getLockedBy();
            }
        }
        return null;
    }






    /**
     * 更新资源占用的节点集合
     *
     * @param nodeSet     节点结合
     * @param addOrRemove 添加为true, 删除为false
     */
    private static synchronized <T> void updateLockedNodes(Set<Coord> nodeSet, boolean addOrRemove, T t) {
        List<LockNode> lockList = new ArrayList<>();
        if (addOrRemove) {
            for (Coord node : nodeSet) {
                boolean exist = false;
                for (LockNode lockNode : lockedNodes) {
                    if (lockNode.equals(node)) {
                        exist = true;
                        if (!lockNode.getLockedBy().equals(t)) {
                            log.error("占用失败, 该节点已被Rgv_" + lockNode.getLockedBy() + "占用");
                            throw new IllegalArgumentException(t
                                    + "占用失败, 该节点已被Rgv_" + lockNode.getLockedBy() + "占用");
                        }
                    }
                }
                if (!exist) {
                    lockedNodes.add(new LockNode<>(node, t));
                    lockList.add(new LockNode(node, t));
                }
            }
            if (!lockList.isEmpty()) {
                StringBuilder stringBuilder = new StringBuilder(t.toString() + "锁定以下点:");
                lockList.forEach(lockNode -> stringBuilder.append(lockNode.toString()));
                log.info(stringBuilder.toString());
            }
        } else {
            List<LockNode> removeList = new ArrayList<>();
            for (Coord node : nodeSet) {
                for (LockNode lockNode : lockedNodes) {
                    if (lockNode.equals(node) && lockNode.getLockedBy().equals(t)) {
                        // 设置改节点临时占用解除
                        removeList.add(lockNode);
                    }
                }
            }
            lockedNodes.removeAll(removeList);
            if (!removeList.isEmpty()) {
                StringBuilder stringBuilder = new StringBuilder(t.toString() + "解锁以下点:");
                removeList.forEach(lockNode -> stringBuilder.append(lockNode.toString()));
                log.info(stringBuilder.toString());
            }
        }
    }




    public static synchronized <T> void lockedNodes(Set<Coord> nodeSet, T t) {
        updateLockedNodes(nodeSet, true, t);
    }

    public static synchronized <T> void unlockedNodes(Set<Coord> nodeSet, T t) {
        updateLockedNodes(nodeSet, false, t);
    }

    public static synchronized <T> boolean lockedNodesSafe(Set<Coord> nodeSet, T t) {
        if (isLocked(nodeSet) != null && !isLockedByObj(nodeSet, t)) {
            return false;
        }

        lockedNodes(nodeSet, t);
        return true;
    }

    public static synchronized boolean getDeviceLockSafe(Object obj, Set<Coord> nodeSet) {
        for (Coord coord : nodeSet) {
            if (checkDeviceLock(obj, coord) != null) {
                return false;
            }
        }

        for (Coord coord : nodeSet) {
            deviceLockNodes.add(new LockNode(coord, obj));
        }

        return true;
    }

    /**
     * 如果被锁定且锁定对象不是obj，返回锁定对象，否则返回null
     * @param obj
     * @param coord
     * @return
     */
    private static Object checkDeviceLock(Object obj, Coord coord) {
        for (LockNode deviceLockNode : deviceLockNodes) {
            if (deviceLockNode.equals(coord)) {
                if (!deviceLockNode.getLockedBy().equals(obj)) {
                    return deviceLockNode.getLockedBy();
                } else {
                    return null;
                }
            }
        }

        return null;
    }

    public static Object isDeviceLocked(Set<Coord> nodeSet) {
        for (LockNode lockNode : deviceLockNodes) {
            for (Coord node : nodeSet) {
                if (lockNode.equals(node)) {
                    return lockNode.getLockedBy();
                }
            }
        }
        return null;
    }

    /**
     * 解锁设备
     * @param object
     */
    public synchronized static void releaseDeviceLock(Object object) {
        Iterator<LockNode> iterator = deviceLockNodes.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getLockedBy().equals(object)) {
                iterator.remove();
            }
        }
    }


    public static Set<Coord> newSetByList(List<Node> nodes){
        HashSet<Coord> result = new HashSet<>();
        for (Node node : nodes) {
            result.add(node);
        }
        return result;
    }

    /**
     * 插入资源锁(从当前线程获取Rgv对象)
     */
    public static synchronized void insertLocationLock(FourWayRgv rgv, Node curLocation) {
        Set<Coord> locationNode;
        if (Objects.equals(curLocation.getNodeType(), NodeType.L)){
            locationNode = new HashSet<>();
            for (int i = 1; i <= 20; i++) {
                locationNode.add(new Coord(curLocation.getX(),curLocation.getY(),i));
            }
        }else {
             locationNode = Collections.singleton(curLocation);

        }
        if (!isLockedByObj(locationNode, rgv)) {
            addLockedNodes(locationNode, rgv);
        }
    }
    /**
     * 插入资源锁(从当前线程获取Rgv对象)
     */
    public static synchronized void updateLocationLock(FourWayRgv rgv, Coord curLocation) {
        Set<Coord> lockCoordByObj = getLockCoordByObj(rgv);
        lockCoordByObj.remove(curLocation);
        removeLockedNodes(lockCoordByObj,rgv);
        StringBuilder sb = new StringBuilder();
        for (Coord coord : lockCoordByObj) {
            sb.append(coord.toString());
            sb.append(",");
        }
        log.info("{}释放资源:{}",rgv.getName(),sb.toString());

        Set<Coord> locationNode = Collections.singleton(curLocation);
        if (!isLockedByObj(locationNode, rgv)) {
            addLockedNodes(locationNode, rgv);
        }
    }

    /**
     * 锁定提升机
     *
     * @param lifter
     */
    public static <T> void lockLifter(Lifter lifter, T t) {
        waitLockRelease(lifter, t);

        Integer deviceId = liferToDeviceIdMap.get(lifter);
        synchronized (deviceId) {
            waitLockRelease(lifter, t);
            updateLifterLock(lifter, true, t);
            log.info("占用提升机： " + lifter + "资源");
        }
    }

    private static <T> void waitLockRelease(Lifter lifter, T t) {
        if (t instanceof FourWayRgv) {
            ((FourWayRgv) t).checkIntercept("LockNodeManager.lockLifter" + lifter.toString());
        }
        if (t instanceof WcsTask) {
            PlcTaskThread.checkPLCInterrupt(((WcsTask) t).getTaskId(), "LockNodeManager.lockLifter");
        }
        while (LockNodeManager.checkLifterLocked(lifter, t)) {
            if (t instanceof FourWayRgv) {
                ((FourWayRgv) t).checkIntercept("LockNodeManager.lockLifter" + lifter.toString());
            }
            if (t instanceof WcsTask) {
                PlcTaskThread.checkPLCInterrupt(((WcsTask) t).getTaskId(), "LockNodeManager.lockLifter");
            }
            WcsUtils.sleep(1000);
        }
    }

    /**
     * 解锁提升机
     *
     * @param lifter
     */
    public static synchronized <T> void unlockLifter(Lifter lifter, T t) {
        updateLifterLock(lifter, false, t);
        log.info("解锁提升机： " + lifter + "资源");
    }

    /**
     * 检查提升机资源是否被占用
     *
     * @param lifter
     * @return
     */
    public static synchronized <T> boolean checkLifterLocked(Lifter lifter, T t) {
        Set<Coord> lifterNodes = new HashSet<>();
        for (int i = 1; i <= 20; i++) {
            lifterNodes.add(new Coord(lifter.getX(), lifter.getY(), i));
        }
        return checkNodesLocked(lifterNodes, t);
    }

    /**
     * 锁定或释放提升机节点资源
     *
     * @param lifter
     */
    private static synchronized <T> void updateLifterLock(Lifter lifter, boolean addOrRemove, T t) {
        Set<Coord> lifterNodes = new HashSet<>();
        for (int i = 1; i <= 20; i++) {
            lifterNodes.add(new Coord(lifter.getX(), lifter.getY(), i));
        }
        updateLockedNodes(lifterNodes, addOrRemove, t);
    }

}
